ZgRNS Git Node
firmware/example_node.py rns-152-frame-hardening (97447676) Text, 9.88 KB
T8b949e"""
µReticulum Example: LXMF Messaging Node
========================================
Compatible with MeshChat, Sideband, NomadNet over UDP on the same LAN.
Usage (MicroPython on ESP32-S3 / RP2040):
1. Edit config.py — set WiFi credentials, node name, and interfaces
2. Copy the urns/ folder, config.py, and this file to the device
3. Uncomment peripherals you have connected below
4. Run with: import example_node
"""
Tff7b72from T7ee787config Tff7b72import Te6edf3WIFI_SSIDTb4b4b4, Te6edf3WIFI_PASSTb4b4b4, Te6edf3NODE_NAMETb4b4b4, Te6edf3DEBUGTb4b4b4, Te6edf3CONFIG
Tff7b72import T7ee787gc
Te6edf3gcTff7b72.Td2a8ffcollectTb4b4b4(Tb4b4b4)
T8b949e# ---- Peripherals ----
T8b949e# Uncomment the ones you have connected. Shared I2C bus for I2C devices.
T8b949e# IMPORTANT: I2C pins must NOT overlap with interface pins (E32 uses
T8b949e# UART1 TX=4, RX=5, AUX=6, M0=15, M1=2). Pick free GPIOs for I2C.
T8b949e# from machine import Pin, SoftI2C
T8b949e# i2c = SoftI2C(scl=Pin(21), sda=Pin(20), freq=100000)
T8b949e# import peripherals.bme280_sensor as bme_sensor
T8b949e# bme_sensor.init(i2c)
T8b949e# import peripherals.neopixel_led as neopixel_led
T8b949e# neopixel_led.init(pin=21)
T8b949e# import peripherals.gpio_control as gpio
T8b949e# gpio.init({"lamp": (2, "OUT")})
T8b949e# Battery: board-declared. init_battery() pulls pin/divider from the board preset
T8b949e# and self-disables if the board has no divider, so listing it is always safe —
T8b949e# it answers "battery"/"sensor" only when a battery is actually wired.
Tff7b72import T7ee787peripheralsT7ee787.T7ee787adc_reader Tff7b72as T7ee787adc_reader
Te6edf3adc_readerTff7b72.Td2a8ffinit_batteryTb4b4b4(Te6edf3CONFIGTb4b4b4)
T8b949e# import peripherals.sds011_sensor as sds011_sensor
T8b949e# sds011_sensor.init(uart_id=1, tx_pin=43, rx_pin=44)
T8b949e# List all active peripherals here (must match uncommented imports above)
Te6edf3active_peripherals Tff7b72= Tb4b4b4[Te6edf3adc_readerTb4b4b4]
Te6edf3gcTff7b72.Td2a8ffcollectTb4b4b4(Tb4b4b4)
T8b949e# ---- Echo reply ----
Te6edf3ECHO_REPLY Tff7b72= Tff7b72True
Tff7b72def Td2a8ff_peer_nameTb4b4b4(Te6edf3routerTb4b4b4, Te6edf3dest_hashTb4b4b4)Tb4b4b4:
T8b949e"""Get display name for a destination hash, or short hex."""
Te6edf3peer Tff7b72= Te6edf3routerTff7b72.Td2a8ffpeersTff7b72.Td2a8ffgetTb4b4b4(Te6edf3dest_hashTb4b4b4)
Tff7b72if Te6edf3peer Tff7b72and Te6edf3peerTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6ffnameTa5d6ff"Tb4b4b4)Tb4b4b4:
Tff7b72return Te6edf3peerTb4b4b4[Ta5d6ff"Ta5d6ffnameTa5d6ff"Tb4b4b4]
Tff7b72return Te6edf3dest_hashTff7b72.Td2a8ffhexTb4b4b4(Tb4b4b4)Tb4b4b4[Tb4b4b4:T79c0ff8Tb4b4b4]
Tff7b72async Tff7b72def Td2a8ff_send_msgTb4b4b4(Te6edf3routerTb4b4b4, Te6edf3dest_hashTb4b4b4, Te6edf3bodyTb4b4b4)Tb4b4b4:
T8b949e"""Send LXMF message as async task (crypto is slow)."""
Tff7b72import T7ee787uasyncio Tff7b72as T7ee787asyncio
Tff7b72await Te6edf3asyncioTff7b72.Td2a8ffsleepTb4b4b4(T79c0ff0Tb4b4b4)
Tff7b72tryTb4b4b4:
Te6edf3msg Tff7b72= Te6edf3routerTff7b72.Td2a8ffsend_messageTb4b4b4(Te6edf3dest_hashTb4b4b4, Te6edf3bodyTb4b4b4)
Tff7b72if Te6edf3msgTb4b4b4:
Tffa657printTb4b4b4(Ta5d6ff"Ta5d6ff[Sent] -> Ta5d6ff" Tff7b72+ Te6edf3dest_hashTff7b72.Td2a8ffhexTb4b4b4(Tb4b4b4)Tb4b4b4[Tb4b4b4:T79c0ff8Tb4b4b4]Tb4b4b4)
Tff7b72elseTb4b4b4:
Tffa657printTb4b4b4(Ta5d6ff"Ta5d6ff[Error] Unknown identity: Ta5d6ff" Tff7b72+ Te6edf3dest_hashTff7b72.Td2a8ffhexTb4b4b4(Tb4b4b4)Tb4b4b4[Tb4b4b4:T79c0ff8Tb4b4b4]Tb4b4b4)
Tff7b72except Tf85149Exception Tff7b72as Te6edf3eTb4b4b4:
Tffa657printTb4b4b4(Ta5d6ff"Ta5d6ff[Error] Send failed: Ta5d6ff" Tff7b72+ Tffa657strTb4b4b4(Te6edf3eTb4b4b4)Tb4b4b4)
Te6edf3gcTff7b72.Td2a8ffcollectTb4b4b4(Tb4b4b4)
Tff7b72async Tff7b72def Td2a8ffserial_input_loopTb4b4b4(Te6edf3routerTb4b4b4)Tb4b4b4:
T8b949e"""Poll stdin for /msg <hash> <body> commands."""
Tff7b72import T7ee787sys
Tff7b72import T7ee787select
Tff7b72import T7ee787uasyncio Tff7b72as T7ee787asyncio
Tff7b72from T7ee787urnsT7ee787.T7ee787identity Tff7b72import Te6edf3Identity
Te6edf3poller Tff7b72= Te6edf3selectTff7b72.Td2a8ffpollTb4b4b4(Tb4b4b4)
Te6edf3pollerTff7b72.Td2a8ffregisterTb4b4b4(Te6edf3sysTff7b72.Td2a8ffstdinTb4b4b4, Te6edf3selectTff7b72.Td2a8ffPOLLINTb4b4b4)
Te6edf3buf Tff7b72= Ta5d6ff"Ta5d6ff"
Tff7b72while Tff7b72TrueTb4b4b4:
Tff7b72if Te6edf3pollerTff7b72.Td2a8ffpollTb4b4b4(T79c0ff0Tb4b4b4)Tb4b4b4:
Te6edf3ch Tff7b72= Te6edf3sysTff7b72.Td2a8ffstdinTff7b72.Td2a8ffreadTb4b4b4(T79c0ff1Tb4b4b4)
Tff7b72if Te6edf3ch Tff7b72in Tb4b4b4(Ta5d6ff"Tffea00\nTa5d6ff"Tb4b4b4, Ta5d6ff"Tffea00\rTa5d6ff"Tb4b4b4)Tb4b4b4:
Te6edf3line Tff7b72= Te6edf3bufTff7b72.Td2a8ffstripTb4b4b4(Tb4b4b4)
Te6edf3buf Tff7b72= Ta5d6ff"Ta5d6ff"
Tff7b72if Te6edf3lineTff7b72.Td2a8ffstartswithTb4b4b4(Ta5d6ff"Ta5d6ff/msg Ta5d6ff"Tb4b4b4)Tb4b4b4:
Te6edf3parts Tff7b72= Te6edf3lineTb4b4b4[T79c0ff5Tb4b4b4:Tb4b4b4]Tff7b72.Td2a8ffsplitTb4b4b4(Ta5d6ff"Ta5d6ff Ta5d6ff"Tb4b4b4, T79c0ff1Tb4b4b4)
Tff7b72if Tffa657lenTb4b4b4(Te6edf3partsTb4b4b4) Tff7b72< T79c0ff2 Tff7b72or Tffa657lenTb4b4b4(Te6edf3partsTb4b4b4[T79c0ff0Tb4b4b4]Tb4b4b4) Tff7b72< T79c0ff8Tb4b4b4:
Tffa657printTb4b4b4(Ta5d6ff"Ta5d6ffUsage: /msg <hex_hash> <message>Ta5d6ff"Tb4b4b4)
Tff7b72continue
Te6edf3prefix Tff7b72= Te6edf3partsTb4b4b4[T79c0ff0Tb4b4b4]Tff7b72.Td2a8fflowerTb4b4b4(Tb4b4b4)
Te6edf3body Tff7b72= Te6edf3partsTb4b4b4[T79c0ff1Tb4b4b4]
T8b949e# Find destination by hash prefix
Te6edf3match Tff7b72= Tff7b72None
Tff7b72for Te6edf3dh Tff7b72in Te6edf3IdentityTff7b72.Td2a8ffknown_destinationsTb4b4b4:
Tff7b72if Te6edf3dhTff7b72.Td2a8ffhexTb4b4b4(Tb4b4b4)Tff7b72.Td2a8ffstartswithTb4b4b4(Te6edf3prefixTb4b4b4)Tb4b4b4:
Te6edf3match Tff7b72= Te6edf3dh
Tff7b72break
Tff7b72if Te6edf3matchTb4b4b4:
Te6edf3asyncioTff7b72.Td2a8ffcreate_taskTb4b4b4(Te6edf3_send_msgTb4b4b4(Te6edf3routerTb4b4b4, Te6edf3matchTb4b4b4, Te6edf3bodyTb4b4b4)Tb4b4b4)
Tff7b72elseTb4b4b4:
Tffa657printTb4b4b4(Ta5d6ff"Ta5d6ff[Error] No known destination: Ta5d6ff" Tff7b72+ Te6edf3prefixTb4b4b4)
Tff7b72elif Te6edf3lineTb4b4b4:
Tffa657printTb4b4b4(Ta5d6ff"Ta5d6ffUnknown command. Use: /msg <hash> <message>Ta5d6ff"Tb4b4b4)
Tff7b72elseTb4b4b4:
Te6edf3buf Tff7b72+Tff7b72= Te6edf3ch
Tff7b72await Te6edf3asyncioTff7b72.Td2a8ffsleepTb4b4b4(T79c0ff0.05Tb4b4b4)
Tff7b72async Tff7b72def Td2a8ffsend_echo_replyTb4b4b4(Te6edf3routerTb4b4b4, Te6edf3source_hashTb4b4b4, Te6edf3contentTb4b4b4)Tb4b4b4:
T8b949e"""Send echo reply as async task (crypto takes ~7s, must not block poll loop)."""
Tff7b72import T7ee787uasyncio Tff7b72as T7ee787asyncio
T8b949e# Yield so the poll loop can resume immediately
Tff7b72await Te6edf3asyncioTff7b72.Td2a8ffsleepTb4b4b4(T79c0ff0Tb4b4b4)
Tff7b72tryTb4b4b4:
Te6edf3reply_content Tff7b72= Ta5d6ff"Ta5d6ffEcho: Ta5d6ff" Tff7b72+ Tffa657strTb4b4b4(Te6edf3contentTb4b4b4)
Te6edf3msg Tff7b72= Te6edf3routerTff7b72.Td2a8ffsend_messageTb4b4b4(Te6edf3source_hashTb4b4b4, Te6edf3reply_contentTb4b4b4)
Tff7b72if Te6edf3msgTb4b4b4:
Tff7b72if Te6edf3DEBUG Tff7b72>Tff7b72= T79c0ff1Tb4b4b4:
Tffa657printTb4b4b4(Ta5d6ff"Ta5d6ff[Echo] Replied to Ta5d6ff" Tff7b72+ Te6edf3source_hashTff7b72.Td2a8ffhexTb4b4b4(Tb4b4b4)Tb4b4b4[Tb4b4b4:T79c0ff8Tb4b4b4]Tb4b4b4)
Tff7b72elseTb4b4b4:
Tff7b72if Te6edf3DEBUG Tff7b72>Tff7b72= T79c0ff1Tb4b4b4:
Tffa657printTb4b4b4(Ta5d6ff"Ta5d6ff[Echo] Cannot reply to Ta5d6ff" Tff7b72+ Te6edf3source_hashTff7b72.Td2a8ffhexTb4b4b4(Tb4b4b4)Tb4b4b4[Tb4b4b4:T79c0ff8Tb4b4b4] Tff7b72+ Ta5d6ff"Ta5d6ff (unknown identity)Ta5d6ff"Tb4b4b4)
Tff7b72except Tf85149Exception Tff7b72as Te6edf3eTb4b4b4:
Tff7b72from T7ee787urnsT7ee787.T7ee787log Tff7b72import Te6edf3logTb4b4b4, Te6edf3LOG_ERROR
Te6edf3logTb4b4b4(Ta5d6ff"Ta5d6ffEcho reply error: Ta5d6ff" Tff7b72+ Tffa657strTb4b4b4(Te6edf3eTb4b4b4)Tb4b4b4, Te6edf3LOG_ERRORTb4b4b4)
Te6edf3gcTff7b72.Td2a8ffcollectTb4b4b4(Tb4b4b4)
Tff7b72def Td2a8ffconnect_wifiTb4b4b4(Te6edf3ssidTb4b4b4, Te6edf3passwordTb4b4b4, Te6edf3timeoutTff7b72=T79c0ff15Tb4b4b4)Tb4b4b4:
Tff7b72import T7ee787sys
Tff7b72import T7ee787network
Tff7b72import T7ee787time
Te6edf3platform Tff7b72= Te6edf3sysTff7b72.Td2a8ffplatform T8b949e# "esp32" or "rp2"
T8b949e# ESP32-S3: deactivate AP interface — dual-interface mode routes broadcast
T8b949e# packets to AP instead of STA, preventing UDP broadcast reception.
Tff7b72if Te6edf3platform Tff7b72== Ta5d6ff"Ta5d6ffesp32Ta5d6ff"Tb4b4b4:
Te6edf3ap Tff7b72= Te6edf3networkTff7b72.Td2a8ffWLANTb4b4b4(Te6edf3networkTff7b72.Td2a8ffAP_IFTb4b4b4)
Tff7b72if Te6edf3apTff7b72.Td2a8ffactiveTb4b4b4(Tb4b4b4)Tb4b4b4:
Te6edf3apTff7b72.Td2a8ffactiveTb4b4b4(Tff7b72FalseTb4b4b4)
Tff7b72if Te6edf3DEBUG Tff7b72>Tff7b72= T79c0ff2Tb4b4b4:
Tffa657printTb4b4b4(Ta5d6ff"Ta5d6ffAP_IF deactivatedTa5d6ff"Tb4b4b4)
Te6edf3wlan Tff7b72= Te6edf3networkTff7b72.Td2a8ffWLANTb4b4b4(Te6edf3networkTff7b72.Td2a8ffSTA_IFTb4b4b4)
Te6edf3wlanTff7b72.Td2a8ffactiveTb4b4b4(Tff7b72TrueTb4b4b4)
Tff7b72if Tff7b72not Te6edf3wlanTff7b72.Td2a8ffisconnectedTb4b4b4(Tb4b4b4)Tb4b4b4:
Tff7b72if Te6edf3DEBUG Tff7b72>Tff7b72= T79c0ff1Tb4b4b4:
Tffa657printTb4b4b4(Ta5d6ff"Ta5d6ffConnecting to WiFi:Ta5d6ff"Tb4b4b4, Te6edf3ssidTb4b4b4)
Te6edf3wlanTff7b72.Td2a8ffconnectTb4b4b4(Te6edf3ssidTb4b4b4, Te6edf3passwordTb4b4b4)
Te6edf3start Tff7b72= Te6edf3timeTff7b72.Td2a8fftimeTb4b4b4(Tb4b4b4)
Tff7b72while Tff7b72not Te6edf3wlanTff7b72.Td2a8ffisconnectedTb4b4b4(Tb4b4b4)Tb4b4b4:
Tff7b72if Te6edf3timeTff7b72.Td2a8fftimeTb4b4b4(Tb4b4b4) Tff7b72- Te6edf3start Tff7b72> Te6edf3timeoutTb4b4b4:
Tff7b72raise Tf85149RuntimeErrorTb4b4b4(Ta5d6ff"Ta5d6ffWiFi connection timed outTa5d6ff"Tb4b4b4)
Te6edf3timeTff7b72.Td2a8ffsleepTb4b4b4(T79c0ff0.5Tb4b4b4)
T8b949e# Disable WiFi power management to reliably receive broadcast UDP
Tff7b72if Te6edf3platform Tff7b72== Ta5d6ff"Ta5d6ffesp32Ta5d6ff"Tb4b4b4:
Te6edf3wlanTff7b72.Td2a8ffconfigTb4b4b4(Te6edf3pmTff7b72=T79c0ff0Tb4b4b4)
Tff7b72elif Te6edf3platform Tff7b72== Ta5d6ff"Ta5d6ffrp2Ta5d6ff"Tb4b4b4:
Te6edf3wlanTff7b72.Td2a8ffconfigTb4b4b4(Te6edf3pmTff7b72=T79c0ff0xa11140Tb4b4b4)
Te6edf3ip Tff7b72= Te6edf3wlanTff7b72.Td2a8ffifconfigTb4b4b4(Tb4b4b4)Tb4b4b4[T79c0ff0Tb4b4b4]
Tff7b72if Te6edf3DEBUG Tff7b72>Tff7b72= T79c0ff1Tb4b4b4:
Tffa657printTb4b4b4(Ta5d6ff"Ta5d6ffConnected! IP:Ta5d6ff"Tb4b4b4, Te6edf3ipTb4b4b4, Ta5d6ff"Ta5d6ff(Ta5d6ff" Tff7b72+ Te6edf3platform Tff7b72+ Ta5d6ff"Ta5d6ff)Ta5d6ff"Tb4b4b4)
Tff7b72tryTb4b4b4:
Tff7b72import T7ee787ntptime
Te6edf3ntptimeTff7b72.Td2a8ffsettimeTb4b4b4(Tb4b4b4)
Tff7b72if Te6edf3DEBUG Tff7b72>Tff7b72= T79c0ff1Tb4b4b4:
Tffa657printTb4b4b4(Ta5d6ff"Ta5d6ffNTP syncedTa5d6ff"Tb4b4b4)
Tff7b72except Tf85149Exception Tff7b72as Te6edf3eTb4b4b4:
Tff7b72if Te6edf3DEBUG Tff7b72>Tff7b72= T79c0ff1Tb4b4b4:
Tffa657printTb4b4b4(Ta5d6ff"Ta5d6ffNTP sync failed:Ta5d6ff"Tb4b4b4, Te6edf3eTb4b4b4)
Tff7b72return Te6edf3ip
Tff7b72def Td2a8ffsetup_nodeTb4b4b4(Te6edf3rnsTb4b4b4, Te6edf3node_nameTb4b4b4)Tb4b4b4:
Tff7b72from T7ee787urnsT7ee787.T7ee787lxmf Tff7b72import Te6edf3LXMRouter
Te6edf3router Tff7b72= Te6edf3LXMRouterTb4b4b4(Te6edf3identityTff7b72=Te6edf3rnsTff7b72.Td2a8ffidentityTb4b4b4)
Te6edf3dest Tff7b72= Te6edf3routerTff7b72.Td2a8ffregister_delivery_identityTb4b4b4(Te6edf3rnsTff7b72.Td2a8ffidentityTb4b4b4, Te6edf3display_nameTff7b72=Te6edf3node_nameTb4b4b4)
T8b949e# Incoming LXMF message handler (proof is sent automatically by LXMRouter)
Tff7b72def Td2a8ffon_messageTb4b4b4(Te6edf3messageTb4b4b4)Tb4b4b4:
Tff7b72import T7ee787uasyncio Tff7b72as T7ee787asyncio
Te6edf3verified Tff7b72= Ta5d6ff"Ta5d6ffverifiedTa5d6ff" Tff7b72if Te6edf3messageTff7b72.Td2a8ffsignature_validated Tff7b72else Ta5d6ff"Ta5d6ffUNVERIFIEDTa5d6ff"
Te6edf3sender Tff7b72= Te6edf3messageTff7b72.Td2a8ffsource_hashTff7b72.Td2a8ffhexTb4b4b4(Tb4b4b4)Tb4b4b4[Tb4b4b4:T79c0ff8Tb4b4b4]
Te6edf3content Tff7b72= Te6edf3messageTff7b72.Td2a8ffcontent_as_stringTb4b4b4(Tb4b4b4) Tff7b72or Ta5d6ff"Ta5d6ff(binary)Ta5d6ff"
T8b949e# Run content through active peripherals
Te6edf3results Tff7b72= Tb4b4b4[Tb4b4b4]
Tff7b72for Te6edf3p Tff7b72in Te6edf3active_peripheralsTb4b4b4:
Te6edf3result Tff7b72= Te6edf3pTff7b72.Td2a8ffprocessTb4b4b4(Te6edf3contentTb4b4b4)
Tff7b72if Te6edf3resultTb4b4b4:
Te6edf3resultsTff7b72.Td2a8ffappendTb4b4b4(Te6edf3resultTb4b4b4)
Tff7b72if Te6edf3resultsTb4b4b4:
Te6edf3content Tff7b72= Ta5d6ff"Tffea00\nTa5d6ff"Tff7b72.Td2a8ffjoinTb4b4b4(Te6edf3resultsTb4b4b4)
Tff7b72if Te6edf3DEBUG Tff7b72>Tff7b72= T79c0ff1Tb4b4b4:
Te6edf3name Tff7b72= Te6edf3_peer_nameTb4b4b4(Te6edf3routerTb4b4b4, Te6edf3messageTff7b72.Td2a8ffsource_hashTb4b4b4)
Tffa657printTb4b4b4(Tb4b4b4)
Tffa657printTb4b4b4(Ta5d6ff"Ta5d6ff<Ta5d6ff" Tff7b72+ Te6edf3name Tff7b72+ Ta5d6ff"Ta5d6ff/Ta5d6ff" Tff7b72+ Te6edf3sender Tff7b72+ Ta5d6ff"Ta5d6ff> Ta5d6ff" Tff7b72+ Te6edf3contentTb4b4b4)
T8b949e# Queue async echo reply (non-blocking)
Tff7b72if Te6edf3ECHO_REPLYTb4b4b4:
Te6edf3asyncioTff7b72.Td2a8ffcreate_taskTb4b4b4(Te6edf3send_echo_replyTb4b4b4(Te6edf3routerTb4b4b4, Te6edf3messageTff7b72.Td2a8ffsource_hashTb4b4b4, Te6edf3contentTb4b4b4)Tb4b4b4)
Te6edf3gcTff7b72.Td2a8ffcollectTb4b4b4(Tb4b4b4)
Te6edf3routerTff7b72.Td2a8ffregister_delivery_callbackTb4b4b4(Te6edf3on_messageTb4b4b4)
T8b949e# Announce handler — see other LXMF peers
Tff7b72def Td2a8ffon_announceTb4b4b4(Te6edf3destination_hashTb4b4b4, Te6edf3display_nameTb4b4b4)Tb4b4b4:
Tff7b72if Te6edf3DEBUG Tff7b72>Tff7b72= T79c0ff1Tb4b4b4:
Tffa657printTb4b4b4(Ta5d6ff"Ta5d6ff[Peer] Ta5d6ff" Tff7b72+ Tb4b4b4(Te6edf3display_name Tff7b72or Ta5d6ff"Ta5d6ff?Ta5d6ff"Tb4b4b4) Tff7b72+ Ta5d6ff"Ta5d6ff [Ta5d6ff" Tff7b72+ Te6edf3destination_hashTff7b72.Td2a8ffhexTb4b4b4(Tb4b4b4)Tb4b4b4[Tb4b4b4:T79c0ff8Tb4b4b4] Tff7b72+ Ta5d6ff"Ta5d6ff]Ta5d6ff"Tb4b4b4)
Te6edf3routerTff7b72.Td2a8ffregister_announce_callbackTb4b4b4(Te6edf3on_announceTb4b4b4)
Tff7b72return Te6edf3destTb4b4b4, Te6edf3router
Tff7b72def Td2a8ffneeds_wifiTb4b4b4(Te6edf3configTb4b4b4)Tb4b4b4:
T8b949e"""Check if any UDP or TCP interface is enabled in config."""
Tff7b72for Te6edf3iface Tff7b72in Te6edf3configTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6ffinterfacesTa5d6ff"Tb4b4b4, Tb4b4b4[Tb4b4b4]Tb4b4b4)Tb4b4b4:
Tff7b72if Te6edf3ifaceTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6ffenabledTa5d6ff"Tb4b4b4, Tff7b72FalseTb4b4b4) Tff7b72and Te6edf3ifaceTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6fftypeTa5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6ff"Tb4b4b4) Tff7b72in Tb4b4b4(
Ta5d6ff"Ta5d6ffUDPInterfaceTa5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6ffTCPClientInterfaceTa5d6ff"Tb4b4b4,
Tb4b4b4)Tb4b4b4:
Tff7b72return Tff7b72True
Tff7b72return Tff7b72False
Tff7b72def Td2a8ffmainTb4b4b4(Tb4b4b4)Tb4b4b4:
T8b949e"""Run on MicroPython (ESP32-S3, RP2040, etc.)"""
Tff7b72import T7ee787uasyncio Tff7b72as T7ee787asyncio
Tff7b72if Te6edf3needs_wifiTb4b4b4(Te6edf3CONFIGTb4b4b4)Tb4b4b4:
Te6edf3ip Tff7b72= Te6edf3connect_wifiTb4b4b4(Te6edf3WIFI_SSIDTb4b4b4, Te6edf3WIFI_PASSTb4b4b4)
Te6edf3gcTff7b72.Td2a8ffcollectTb4b4b4(Tb4b4b4)
Tff7b72from T7ee787urns Tff7b72import Te6edf3Reticulum
Tff7b72from T7ee787urnsT7ee787.T7ee787log Tff7b72import Te6edf3LOG_NONETb4b4b4, Te6edf3LOG_NOTICETb4b4b4, Te6edf3LOG_DEBUG
Te6edf3log_map Tff7b72= Tb4b4b4{T79c0ff0Tb4b4b4: Te6edf3LOG_NONETb4b4b4, T79c0ff1Tb4b4b4: Te6edf3LOG_NONETb4b4b4, T79c0ff2Tb4b4b4: Te6edf3LOG_DEBUGTb4b4b4}
Te6edf3rns Tff7b72= Te6edf3ReticulumTb4b4b4(Te6edf3loglevelTff7b72=Te6edf3log_mapTff7b72.Td2a8ffgetTb4b4b4(Te6edf3DEBUGTb4b4b4, Te6edf3LOG_NOTICETb4b4b4)Tb4b4b4)
Te6edf3rnsTff7b72.Td2a8ffconfig Tff7b72= Te6edf3CONFIG
Te6edf3destTb4b4b4, Te6edf3router Tff7b72= Te6edf3setup_nodeTb4b4b4(Te6edf3rnsTb4b4b4, Te6edf3NODE_NAMETb4b4b4)
Te6edf3gcTff7b72.Td2a8ffcollectTb4b4b4(Tb4b4b4)
Te6edf3rnsTff7b72.Td2a8ffsetup_interfacesTb4b4b4(Tb4b4b4)
Te6edf3gcTff7b72.Td2a8ffcollectTb4b4b4(Tb4b4b4)
Tff7b72if Te6edf3DEBUG Tff7b72>Tff7b72= T79c0ff1Tb4b4b4:
Tffa657printTb4b4b4(Ta5d6ff"Ta5d6ffLXMF address:Ta5d6ff"Tb4b4b4, Te6edf3destTff7b72.Td2a8ffhexhashTb4b4b4)
Tffa657printTb4b4b4(Ta5d6ff"Ta5d6ffFree memory:Ta5d6ff"Tb4b4b4, Te6edf3gcTff7b72.Td2a8ffmem_freeTb4b4b4(Tb4b4b4)Tb4b4b4, Ta5d6ff"Ta5d6ffbytesTa5d6ff"Tb4b4b4)
Tffa657printTb4b4b4(Ta5d6ff"Ta5d6ffRunning... (Ctrl+C to stop)Ta5d6ff"Tb4b4b4)
T8b949e# Deferred initial announce — runs AFTER poll loop starts
Tff7b72async Tff7b72def Td2a8ffinitial_announceTb4b4b4(Tb4b4b4)Tb4b4b4:
Tff7b72await Te6edf3asyncioTff7b72.Td2a8ffsleepTb4b4b4(T79c0ff0.5Tb4b4b4)
Tff7b72tryTb4b4b4:
Te6edf3routerTff7b72.Td2a8ffannounceTb4b4b4(Tb4b4b4)
Tff7b72if Te6edf3DEBUG Tff7b72>Tff7b72= T79c0ff1Tb4b4b4:
Tffa657printTb4b4b4(Ta5d6ff"Ta5d6ffAnnounced as:Ta5d6ff"Tb4b4b4, Te6edf3NODE_NAMETb4b4b4)
Tff7b72except Tf85149Exception Tff7b72as Te6edf3eTb4b4b4:
Tff7b72if Te6edf3DEBUG Tff7b72>Tff7b72= T79c0ff2Tb4b4b4:
Tffa657printTb4b4b4(Ta5d6ff"Ta5d6ffInitial announce error:Ta5d6ff"Tb4b4b4, Te6edf3eTb4b4b4)
Te6edf3gcTff7b72.Td2a8ffcollectTb4b4b4(Tb4b4b4)
T8b949e# Add periodic re-announce to the event loop
Tff7b72async Tff7b72def Td2a8ffreannounce_loopTb4b4b4(Tb4b4b4)Tb4b4b4:
Tff7b72while Tff7b72TrueTb4b4b4:
Tff7b72await Te6edf3asyncioTff7b72.Td2a8ffsleepTb4b4b4(T79c0ff120Tb4b4b4)
Tff7b72tryTb4b4b4:
Te6edf3routerTff7b72.Td2a8ffannounceTb4b4b4(Tb4b4b4)
Tff7b72if Te6edf3DEBUG Tff7b72>Tff7b72= T79c0ff2Tb4b4b4:
Tffa657printTb4b4b4(Ta5d6ff"Ta5d6ff[Re-announced]Ta5d6ff"Tb4b4b4)
Tff7b72except Tf85149Exception Tff7b72as Te6edf3eTb4b4b4:
Tff7b72if Te6edf3DEBUG Tff7b72>Tff7b72= T79c0ff2Tb4b4b4:
Tffa657printTb4b4b4(Ta5d6ff"Ta5d6ffRe-announce error:Ta5d6ff"Tb4b4b4, Te6edf3eTb4b4b4)
Te6edf3gcTff7b72.Td2a8ffcollectTb4b4b4(Tb4b4b4)
T8b949e# Patch rns.run to include our announce + reannounce tasks
Te6edf3_original_run Tff7b72= Te6edf3rnsTff7b72.Td2a8ffrun
Tff7b72async Tff7b72def Td2a8ffrun_with_reannounceTb4b4b4(Tb4b4b4)Tb4b4b4:
Te6edf3asyncioTff7b72.Td2a8ffcreate_taskTb4b4b4(Te6edf3initial_announceTb4b4b4(Tb4b4b4)Tb4b4b4)
Te6edf3asyncioTff7b72.Td2a8ffcreate_taskTb4b4b4(Te6edf3reannounce_loopTb4b4b4(Tb4b4b4)Tb4b4b4)
Te6edf3asyncioTff7b72.Td2a8ffcreate_taskTb4b4b4(Te6edf3serial_input_loopTb4b4b4(Te6edf3routerTb4b4b4)Tb4b4b4)
T8b949e# Start SDS011 periodic measurement if active
T8b949e# if sds011_sensor.sensor: sds011_sensor.start()
Tff7b72await Te6edf3_original_runTb4b4b4(Tb4b4b4)
Tff7b72tryTb4b4b4:
Te6edf3asyncioTff7b72.Td2a8ffrunTb4b4b4(Te6edf3run_with_reannounceTb4b4b4(Tb4b4b4)Tb4b4b4)
Tff7b72except Tf85149KeyboardInterruptTb4b4b4:
Te6edf3rnsTff7b72.Td2a8ffshutdownTb4b4b4(Tb4b4b4)
Tff7b72if Te6edf3DEBUG Tff7b72>Tff7b72= T79c0ff1Tb4b4b4:
Tffa657printTb4b4b4(Ta5d6ff"Ta5d6ffShutdown completeTa5d6ff"Tb4b4b4)
Te6edf3mainTb4b4b4(Tb4b4b4)
Served by rngit 1.5.2 - Generated in 0.06s